﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace EntregaSO
{
    public abstract class Planificador: IPlanificador
    {
        protected List<Ascensor> ascensores;
        protected int pisos;
        protected Stack<SolicitudAscensor> solicitudes;
        protected Edificio edificio;
        protected EscribirADisco escritor;

        
        public Planificador(int pisos, int cantidadAscensores)
        {
            this.pisos = pisos;
            this.ascensores = new List<Ascensor>();
            this.solicitudes = new Stack<SolicitudAscensor>();
            this.edificio = new Edificio(pisos);
            this.escritor = new EscribirADisco("ResultadosSimulacion.txt");
            //creo ascensores
            CrearAscensores(pisos, cantidadAscensores);
        }
        public IList<Ascensor> Ascensores
        {
            get { return ascensores; }
        }
        public EscribirADisco Escritor
        {
            get { return this.escritor; }
        }
        public void CrearSolicitud(Object piso)
        {
            lock (this)
            {
                SolicitudAscensor s = new SolicitudAscensor((int)piso);
                this.LlamarAscensor(s);
            }
        }
        protected virtual void CrearAscensores(int pisos, int cantidadAscensores) 
        {
             Ascensor a;
            for (int i = 0; i < cantidadAscensores; i++)
            {
                a = new Ascensor(pisos, this, "Ascensores" + i);
                ascensores.Add(a);
            }
        }
        //inicio 
        public void Start()
        {
            //Thread asc;
            foreach (Ascensor a in ascensores)
            {
                a.Avanzar();
                //asc = new Thread(AvanzarAscensor);
                //asc.Start(a);
                //asc.Join();
            }
        }
        public Piso ObtenerPiso(int piso)
        {
            return this.edificio.ObtenerPiso(piso);
        }
        /// <summary>
        /// Llama un ascensor al piso solicitado 
        /// </summary>
        /// <param name="piso">Piso en el cual se solicita el ascensor</param>
        public void LlamarAscensor(SolicitudAscensor solicitud)
        {
            lock (this)
            {
                solicitudes.Push(solicitud);
                Monitor.PulseAll(this);
            }
            
        }
        /// <summary>
        /// Indica si aun hay pedidos en la cola de solicitudes.
        /// </summary>
        /// <returns></returns>
        public bool HayPedidos()
        {
            return (this.solicitudes.Count != 0);
        }
        /// <summary>
        /// Crea un usuario y un hilo para las solicitudes
        /// </summary>
        /// <param name="pisoOrigen"></param>
        /// <param name="pisoDestino"></param>
        /// <param name="peso"></param>
        public void CrearUsuario(int pisoOrigen, int pisoDestino, int peso)
        {
            Usuario u = new Usuario(peso, pisoDestino);
            this.edificio.ObtenerPiso(pisoOrigen).Usuarios.Add(u);
            u.tiempoEnPiso.EmpezarAContar();
            //Creo el hilo
            //Thread t = new Thread(CrearSolicitud);
            //t.Start(pisoOrigen);
            //t.Join();
        }
        public virtual void Planificar()
        {
            SolicitudAscensor s;
            int pisoActual;
            int distancia = int.MaxValue;
            int distanciaAux = int.MaxValue;
            Ascensor asignacion = ascensores.First();
            lock (this)
            {
                //agregar monitor
                while (solicitudes.Count == 0)
                {
                    Monitor.Wait(this);
                }
                s = solicitudes.Pop();
                Monitor.PulseAll(this);
            }
            foreach (Ascensor a in ascensores)
            {
                pisoActual = a.Piso;
                switch (a.Sentido)
                {
                    case Ascensor.SentidoAscensor.Baja:
                        if (pisoActual > s.PisoOrigen)
                        {
                            distanciaAux = pisoActual - s.PisoOrigen;
                        }
                        else
                        {
                            distanciaAux = pisoActual + s.PisoOrigen;
                        }
                        break;
                    case Ascensor.SentidoAscensor.Sube:
                        if (pisoActual < s.PisoOrigen)
                        {
                            distanciaAux = s.PisoOrigen - pisoActual;
                        }
                        else
                        {
                            distanciaAux = (this.pisos - pisoActual) + (this.pisos - s.PisoOrigen);
                        }
                        break;
                    case Ascensor.SentidoAscensor.Quieto:
                        distanciaAux = Math.Abs(pisoActual - s.PisoOrigen);
                        break;
                }
                if (distanciaAux < distancia)
                {
                    asignacion = a;
                    distancia = distanciaAux;
                }
            }
            asignacion.LlamarAscensor(s);
        }
    }
}
